#include "CrossoverOperators.h"

namespace jcrada {


    //REGULAR CROSSOVER

    void RegularCrossover::crossover( TreeOfNodes& son, const TreeOfNodes& daughter ) {
        Node* son_crossover = crossoverPoint(son);
        Node* parent = son_crossover->getParent();
        int parent_arity = parent ? parent->getChildPosition(son_crossover) : -1;
        delete son_crossover;

        Node* daughter_crossover = crossoverPoint(daughter)->clone();

        if (parent == NULL) {
            son.setRoot(daughter_crossover);
        } else {
            daughter_crossover->moveTo(parent, parent_arity);
        }
    }

    //FAIR-SIZE CROSSOVER 

    void FairSizeCrossover::crossover( TreeOfNodes& son, const TreeOfNodes& daughter ) {
        Node* son_crossover = crossoverPoint(son);

        int son_depth = son_crossover->getDepth();

        int min_depth = son_depth - (son_depth / 4);
        int max_depth = son_depth + (son_depth / 4);

        Node* crossover_point = NULL;
        float difference = 1.0 / 0.0;
        Node* candidate = NULL;
        int candidate_depth = 0;
        int max_retries = daughter.getSize() / 2;
        do {
            candidate = crossoverPoint(daughter);
            candidate_depth = candidate->getDepth();
            if (candidate_depth >= min_depth && candidate_depth <= max_depth) {
                crossover_point = candidate;
                break;
            } else if (candidate_depth < min_depth) {
                if (min_depth - candidate_depth < difference) {
                    difference = min_depth - candidate_depth;
                    crossover_point = candidate;
                }
            } else if (candidate_depth > max_depth) {
                if (candidate_depth - max_depth < difference) {
                    difference = candidate_depth - max_depth;
                    crossover_point = candidate;
                }
            }
        } while (--max_retries > 0);


        Node* father = son_crossover->getParent();
        int father_arity = father ? father->getChildPosition(son_crossover) : -1;

        delete son_crossover;

        if (father == NULL) {
            son.setRoot(crossover_point->clone());
        } else {
            crossover_point->clone()->moveTo(father, father_arity);
        }
    }

    //REGULAR AND FAIR-SIZE CROSSOVER

    RegularAndFairSizeCrossover::RegularAndFairSizeCrossover( int preferred_max_size )
    : CrossoverOperator( ),
    _preferred_max_size( preferred_max_size ),
    _random_crossover_method( new RandomGsl( ) ) {

    }

    RegularAndFairSizeCrossover::~RegularAndFairSizeCrossover( ) {
        delete _random_crossover_method;
    }

    void RegularAndFairSizeCrossover::crossover( TreeOfNodes& son, const TreeOfNodes& daughter ) {
        int size = son.getSize();
        double fair_size_probability = (double) size / (double) getPreferredMaxSize();
        if (getRandomCrossoverMethod().nextDouble() < fair_size_probability) {
            _fair_size_crossover.crossover(son, daughter);
        } else {
            _regular_crossover.crossover(son, daughter);
        }
    }

}
